home *** CD-ROM | disk | FTP | other *** search
/ BCI NET / BCI NET Dec 94.iso / archives / telecomm / archivers / aucode.lha / AuDecode.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-02-16  |  26.9 KB  |  734 lines

  1. /* -------------------------------------------------------------------------- **
  2.  * Program: AuDecode
  3.  *
  4.  * UUdecoding for the Amiga.
  5.  *
  6.  *  Written by Christopher R. Hertel
  7.  *  Email: crh@bubble.mooses.affinity.mn.org
  8.  * -------------------------------------------------------------------------- **
  9.  *  Copyright (C) 1994  Christopher R. Hertel
  10.  *
  11.  *  This program is free software; you can redistribute it and/or modify
  12.  *  it under the terms of the GNU General Public License as published by
  13.  *  the Free Software Foundation; either version 2 of the License, or
  14.  *  (at your option) any later version.
  15.  *
  16.  *  This program is distributed in the hope that it will be useful,
  17.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  *  GNU General Public License for more details.
  20.  *
  21.  *  You should have received a copy of the GNU General Public License
  22.  *  along with this program; if not, write to the Free Software
  23.  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24.  *
  25.  * -------------------------------------------------------------------------- **
  26.  *
  27.  * $Log:    AuDecode.c,v $
  28.  * Revision 1.2  94/02/15  21:42:29  CRH
  29.  * Quick change to the construction of the version string.
  30.  * 
  31.  * Revision 1.1  94/02/15  21:39:24  CRH
  32.  * Small fixes to the help messages.
  33.  * ,
  34.  *
  35.  * Revision 1.0  94/02/06  16:26:16  CRH
  36.  * Initial revision
  37.  *
  38.  * -------------------------------------------------------------------------- **
  39.  * My understanding of uuencoding/decoding is based on the knowledge that I
  40.  * gained by reading the uuencode/decode source provided with AmigaUUCP
  41.  * V1.16. Many thanks to the authors of that software!  The following are
  42.  * comments from their code, which are included as my way of giving credit
  43.  * where credit is due.
  44.  *
  45.  * /begin/
  46.  * Written by Mark Horton
  47.  * Modified by ajr (Alan J Rosenthatl,flaps@utcsri.UUCP) to use checksums
  48.  * Modified by fnf (Fred Fish,well!fnf) to use Keith Pyle's suggestion for
  49.  * compatibility
  50.  * Modified by bcn (Bryce Nesbitt,ucbvax!hoser!bryce) to enable CTRL-C for
  51.  * Amiga Lattice C.  Added a transparent file size trailer for later check.
  52.  * Changed fopen from "r" to "rb" for Messy-dos machines (thanks to Andrew
  53.  * Wylie)
  54.  * [uudecode.c:]
  55.  * Modified by bcn (Bryce Nesbitt,ucbvax!cogsci!bryce) to fix a misleading
  56.  * error message on the Amiga port, to fix a bug that prevented decoding
  57.  * certain files, to work even if trailing spaces have been removed from a
  58.  * file, to check the filesize (if present), to add some error checking, to
  59.  * loop for multiple decodes from a single file, and to handle common
  60.  * BITNET mangling.  Kludged around a missing string function in Aztec
  61.  * C. Changed "r" to "rb" and "w" to "wb" for Messy-dos machines
  62.  * (Thanks to Andrew Wylie).
  63.  * /end/
  64.  *
  65.  * -------------------------------------------------------------------------- **
  66.  * This program uses the AuCode module, which contains most of the uudecoding
  67.  * functionality.  90% of *this* file is dedicated to command line parsing,
  68.  * file I/O, and error checking.  This file was written to compile under SAS
  69.  * Amiga C 5.10 or later.  I use the following command line:
  70.  *
  71.  *    lc -L AuDecode.c AuCode.c
  72.  *
  73.  * CRH
  74.  * -------------------------------------------------------------------------- **
  75.  */
  76.  
  77. #include <stdio.h>      /* Standard I/O */
  78. #include <string.h>     /* Standard string functions */
  79. #include <stdlib.h>     /* Standard C functions.  */
  80. #include <ctype.h>      /* C iswhatsit() macros.  */
  81. #include <time.h>       /* C time functions.      */
  82. #include <sys/types.h>  /* Unix types.            */
  83. #include <sys/stat.h>   /* File status functions. */
  84. #include "AuCode.h"     /* Amiga-Usenet coding module. */
  85.  
  86. /* -------------------------------------------------------------------------- **
  87.  * Defines...
  88.  *
  89.  *  FALSE       - Zero, the standard C value for not true.
  90.  *  TRUE        - If it's not false, it must be true.  Mustn't it?
  91.  *
  92.  * MAXCS_WARN   - Maximum number of checksum warning messages to report
  93.  *                before we get annoying.
  94.  */
  95. #define FALSE 0
  96. #define TRUE (~0)
  97. #define MAXCS_WARN 10
  98.  
  99. /* -------------------------------------------------------------------------- **
  100.  * Macros...
  101.  *          Max - This is your basic max() macro.  I implement it here in
  102.  *                order to avoid the inclusion of extra header files.
  103.  */
  104. #define Max(A,B) (((A)>(B))?(A):(B))
  105.  
  106. /* -------------------------------------------------------------------------- **
  107.  * Typedefs...
  108.  *
  109.  *        boolean - Your basic True/False options.
  110.  */
  111.  
  112. typedef char boolean;
  113.  
  114. /* -------------------------------------------------------------------------- **
  115.  * Global constants...
  116.  *  vers    - Program version string.
  117.  *  HelpTxt - An array of help strings.
  118.  */
  119.  
  120. const char *vers = "\0$VER$Id: AuDecode.c,v 1.2 94/02/15 21:42:29 CRH Exp $";
  121.  
  122. const char *HelpTxt[] =
  123.   {
  124.   "Copyright (C) 1994 by  Christopher R. Hertel\n",
  125.   "This program decodes uuencoded files.\n",
  126.   "Usage:",
  127.   "\tAuDecode [options] [infile]\n",
  128.   "where  [infile]  = an input file.  This is the source files that is",
  129.   "                   to be decoded.  If no file is specified, then ",
  130.   "                   standard input will be read.",
  131.   "       [options] = command line switches, as follows:\n",
  132.   "      -? or -h     = display this help message.",
  133.   "      -a           = if the output file already exists, open it in",
  134.   "                     append mode.  The default is to overwrite it.",
  135.   "      -d<n>        = decode specific occurance.  If the input stream",
  136.   "                     contains several encoded files, this switch will",
  137.   "                     allow you to select a single encoded file to",
  138.   "                     decode.  The default is to decode all files.",
  139.   "      -i           = display index only.  This switch causes AuDecode",
  140.   "                     to scan the input for encoded files.  The files",
  141.   "                     *will not be decoded*, but a sequentially numbered",
  142.   "                     list of names will be written to standard output.",
  143.   "      -m<mode>     = output file mode (Unix protection scheme).",
  144.   "                     (Not fully implemented yet.)",
  145.   "      -n<filename> = specify the output filename.  Uuencoded files",
  146.   "                     contain a default target filename, which is",
  147.   "                     generally the same as the input file name received",
  148.   "                     by the encoder.  The decoding program will use this",
  149.   "                     name as the default output filename.  The -n option",
  150.   "                     allows you to override the default target filename.",
  151.   "               Note: If the -d option is not specified, -n assumes -d1.",
  152.   "      -o           = send output to the standard output device (stdout).",
  153.   "                     This option is similar to -n, except that it",
  154.   "                     instructs AuDecode to redirect output to standard",
  155.   "                     output, rather than to a file.  This is useful if",
  156.   "                     you wish to use command line redirection.  This",
  157.   "                     option will override -n.",
  158.   "               Note: Unlike -n, -o does not assume -d1.  If you do not",
  159.   "                     specify an occurance, all encoded files will be",
  160.   "                     decoded to stdout.",
  161.   "      -s           = status display.  The encoder will display the",
  162.   "                     converted byte count (via standard error) as",
  163.   "                     decoding progresses.",
  164.   NULL
  165.   }; /* HelpTxt[] */
  166.  
  167. /* -------------------------------------------------------------------------- **
  168.  * Globals Variables...
  169.  */
  170.  
  171.         /* Process parameters      Default actions                            */
  172. boolean AppendFlg = FALSE;      /* Open output in overwrite mode.             */
  173. boolean DecodeFlg = TRUE;       /* Yes we decode (no, we don't just index).   */
  174. boolean StatusFlg = FALSE;      /* No, we don't display statistics.           */
  175. boolean StdOutFlg = FALSE;      /* Don't send output to stdout.               */
  176. int     OutFlMode = 0;          /* Output file mode.  0=default.              */
  177. int     SelectOcc = 0;          /* Zero means "select all occurances".        */
  178. int     badcsums  = 0;          /* Number of bad checksums encountered.       */
  179.  
  180.         /* File and path specifications.                                      */
  181. char   *OutFlName = NULL;       /* Output file name (default=encoded name).   */
  182. char   *SrcFlName = NULL;       /* Name of input file (default=stdin).        */
  183.  
  184.         /* Processing buffers. */
  185. char    linebufr[au_cdMAX_LINE];    /* Input buffer.    */
  186. char    tmpbufr[au_cdMAX_LINE];     /* Scratch buffer.  */
  187.  
  188.  
  189. /* -------------------------------------------------------------------------- **
  190.  * Functions...
  191.  */
  192.  
  193. static void HelpMsg( void )
  194.   /* ------------------------------------------------------------------------ **
  195.    * This function sends the help message text to stderr.
  196.    *
  197.    *  Notes:  For the Amiga, this code could easily be replaced with calls to
  198.    *          locale.library under OS version 2.1 or greater.
  199.    * ------------------------------------------------------------------------ **
  200.    */
  201.   {
  202.   int i;
  203.  
  204.   printf( "%s\n", &vers[2] );       /* Print version info.  */
  205.   for( i = 0; HelpTxt[i]; i++ )     /* Display each line of help text.  */
  206.     printf( "%s\n", HelpTxt[i] );
  207.   } /* HelpMsg */
  208.  
  209. static boolean ParseCmdLine( int argc, char *argv[] )
  210.   /* ------------------------------------------------------------------------ **
  211.    * This function parses and responds to command line input.
  212.    *
  213.    *  Input:  argc  - Count of arguments in argv.
  214.    *          argv  - An array of pointers to character strings.
  215.    *                  Basically, the previous two are the same standard C
  216.    *                  input parameters that are fed into main().
  217.    *
  218.    *  Output: A boolean.  TRUE if we parsed successfully and can continue
  219.    *          processing, else FALSE.
  220.    *
  221.    *  Notes:  If there *was* no command line input, then we were run from the
  222.    *          workbench.  We don't know how to do that yet, so we error out.
  223.    *          If only one argument, we display a brief message to stderr, and
  224.    *          just keep on going (assume redirected input).
  225.    *          If the first argument is a '?', or if a -h or -? switch is read
  226.    *          from the command line, we will dump the help text and exit.
  227.    * ------------------------------------------------------------------------ **
  228.    */
  229.   {
  230.   int i;
  231.   char *Source;
  232.  
  233.   if( 0 == argc )   /* No arguments = Amiga Workbench */
  234.     return( FALSE );
  235.  
  236.   if( 1 == argc )   /* A signle argument may indicate redirected I/O. */
  237.     {               /* Output to stderr is still safe.  */
  238.     fprintf( stderr, "[%s] Type '%s ?' for help.\n", &vers[2], argv[0] );
  239.     fprintf( stderr, "Copyright (C) 1994 Christopher R. Hertel\n" );
  240.     fprintf( stderr, "Ctrl-C <return> to cancel.\n" );
  241.     return( TRUE );
  242.     }
  243.  
  244.   if( '?' == argv[1][0] )   /* If first character of argv[1] is a '?'...*/
  245.     {                       /* ...then send help (and exit).            */
  246.     HelpMsg();
  247.     return( FALSE );
  248.     }
  249.  
  250.   /* Now we can attempt to parse the command line. */
  251.  
  252.   for( i = 1; i < argc; i++ )
  253.     /* For each command line parameter... */
  254.     {
  255.     Source = argv[i];
  256.     if( '-' == *Source )
  257.       switch( Source[1] )
  258.         {
  259.         case '?':                   /* Dump help text & return */
  260.         case 'H':
  261.         case 'h':
  262.           HelpMsg();
  263.           return( FALSE );
  264.         case 'A':                   /* Open output files in append mode.    */
  265.         case 'a':
  266.           AppendFlg = TRUE;
  267.           break;
  268.         case 'D':                   /* Select a specific occurance.         */
  269.         case 'd':
  270.           sscanf( &(Source[2]), "%d", &(SelectOcc) );
  271.           break;
  272.         case 'I':                   /* Produce index only.                  */
  273.         case 'i':
  274.           DecodeFlg = FALSE;
  275.           break;
  276.         case 'S':                   /* Display status information.          */
  277.         case 's':
  278.           StatusFlg = TRUE;
  279.           break;
  280.         case 'M':                   /* Read mode from cmd line.             */
  281.         case 'm':
  282.           if( OutFlMode )
  283.             fprintf( stderr, "Warning: Multiple -m values; last one wins.\n" );
  284.           sscanf( &Source[2], "%o", &OutFlMode );
  285.           break;
  286.         case 'N':                   /* Set output file name.                */
  287.         case 'n':
  288.           if( StdOutFlg )
  289.             fprintf( stderr, "Warning: -o and -n conflict; -o wins.\n" );
  290.           else
  291.             {
  292.             if( OutFlName )
  293.               {
  294.               fprintf( stderr, "Error: Output filename specified twice.\n" );
  295.               return( FALSE );
  296.               }
  297.             else
  298.               OutFlName = &Source[2];
  299.             }
  300.           break;
  301.         case 'O':                   /* Output to stdout.                    */
  302.         case 'o':
  303.           if( OutFlName )
  304.             {
  305.             fprintf( stderr, "Warning: -o and -n conflict; -o wins.\n" );
  306.             OutFlName = NULL;
  307.             }
  308.           StdOutFlg = TRUE;
  309.           break;
  310.         default:
  311.           fprintf( stderr, "Warning: Unknown option: %s\n", Source );
  312.         } /* switch */
  313.     else                            /* Get input file name.                 */
  314.       {
  315.       if( SrcFlName )
  316.         {
  317.         fprintf( stderr, "Error: Multiple input files specified.\n" );
  318.         fprintf( stderr, "[%s], [%s]\n", SrcFlName, Source );
  319.         fprintf( stderr, "This program handles only one source at a time.  " );
  320.         fprintf( stderr, "Sorry.\n" );
  321.         return( FALSE );
  322.         }
  323.       SrcFlName = Source;
  324.       }
  325.     } /* for */
  326.  
  327.   return( TRUE );
  328.   } /* ParseCmdLine */
  329.  
  330. char *ParseBegin( char *Source, int *modeptr )
  331.   /* ------------------------------------------------------------------------ **
  332.    * This function identifies the file name and file mode that are typically
  333.    * stored in the uuencoded 'begin' line.
  334.    *
  335.    *  Input:  Source  - a pointer to the string buffer that contains the
  336.    *                   'begin' line.
  337.    *          modeptr - a pointer to an integer.  The mode will be read from
  338.    *                    the begin line, and stored in the location indicated
  339.    *                    by modeptr.
  340.    *
  341.    *  Output: A pointer to the file name substring of the begin line.  That
  342.    *          is, a pointer to the position within Source[] at which the file
  343.    *          name is assumed to start.
  344.    *
  345.    *  Notes:  This function will remove any trailing control characters from
  346.    *          the Source string by placing a nul byte ('\0') after the last
  347.    *          non-control character.
  348.    *
  349.    *          This function should probably be moved into the AuCode module.
  350.    * ------------------------------------------------------------------------ **
  351.    */
  352.   {
  353.   int   i;
  354.   char *p;
  355.  
  356.   sscanf( &Source[5], " %o", modeptr );     /* Read Mode */
  357.   for( i = 5; isspace( Source[i] ); i++ );  /* Skip pre-mode spaces.    */
  358.   for(      ; isdigit( Source[i] ); i++ );  /* Skip mode.               */
  359.   for(      ; isspace( Source[i] ); i++ );  /* Skip post-mode spaces.   */
  360.   p = &Source[i];                           /* Should be start of name. */
  361.   for( i = strlen(p); iscntrl(p[i]); --i ); /* Find last non-controlchar*/
  362.   p[i+1] = '\0';                            /* Trim off control chars   */
  363.   return( p );
  364.   } /* ParseBegin */
  365.  
  366. int ParseSize( void )
  367.   /* ------------------------------------------------------------------------ **
  368.    * This function parses the 'size' line that is often included folloing a
  369.    * uuencoded block.
  370.    *
  371.    *  Input:  None.
  372.    *
  373.    *  Ouptut: The size, in bytes, of the original file as reported by the
  374.    *          'size' line, or zero (0) if there was no size line.
  375.    *
  376.    *          This function should probably be moved into the AuCode module.
  377.    * ------------------------------------------------------------------------ **
  378.    */
  379.   {
  380.   int tmp = 0;
  381.  
  382.   if( 0 == strncmp( "size", linebufr, 4 ) )   /* If line starts w/"size"  */
  383.     sscanf( &linebufr[4], " %ld", &tmp );     /* Read size from string.   */
  384.   return( tmp );
  385.   } /* ParseSize */
  386.  
  387. boolean Index( FILE *inF )
  388.   /* ------------------------------------------------------------------------ **
  389.    * This function works its way through the occurance of an encoded output
  390.    * file within the input file.
  391.    *
  392.    *  Input:
  393.    *    inF   - A pointer to the input file (so that we can read from it).
  394.    *
  395.    *  Output:   TRUE if the end of the occurance was found.  FALSE if the
  396.    *            end of the input file was found before the end of the
  397.    *            encoded occurance.
  398.    * ------------------------------------------------------------------------ **
  399.    */
  400.   {
  401.   char *name;
  402.   int   namelen;
  403.   int   mode   = 0;
  404.   int   size   = 0;
  405.   long  bytcnt = 0;
  406.  
  407.   name = ParseBegin( linebufr, &mode );
  408.   namelen = strlen( name );
  409.  
  410.   if( namelen > 35 )                        /* Print file name.         */
  411.     printf( "%-34.34s+  ", name );
  412.   else
  413.     printf( "%-35.*s  ", namelen, name );
  414.  
  415.   if( mode > 0 )
  416.     printf( "%4o  ", mode );                /* Print file mode.         */
  417.   else
  418.     printf( "????  " );                     /* ...or ?s if no mode.     */
  419.  
  420.   if( StatusFlg )
  421.     /* Status "ON" version. */
  422.     {
  423.     int lncnt  = 0;
  424.  
  425.     for(;;)
  426.       {
  427.       if( !(fgets( linebufr, au_cdMAX_LINE, inF )) )
  428.         {                                     /* EOF before end/n = error.*/
  429.         fprintf( stderr,
  430.                  "\nError: Found End-of-File before end of encoded data.\n" );
  431.         return( FALSE );
  432.         }
  433.       if( strncmp( "end\n", linebufr, 4 ) )
  434.         {
  435.         lncnt++;
  436.         bytcnt += au_cdDECD( linebufr[0] );
  437.         if( 0 == (lncnt % 10) )
  438.           printf( "%9ld\b\b\b\b\b\b\b\b\b", bytcnt );
  439.         }
  440.       else
  441.         break;
  442.       }
  443.     }
  444.   else
  445.     /* Status "OFF" version (identical to above, but without status output). */
  446.     {
  447.     for(;;)
  448.       {
  449.       if( !(fgets( linebufr, au_cdMAX_LINE, inF )) )
  450.         {                                     /* EOF before end/n = error.*/
  451.         fprintf( stderr,
  452.                  "\nError: Found End-of-File before end of encoded data.\n" );
  453.         return( FALSE );
  454.         }
  455.       if( strncmp( "end\n", linebufr, 4 ) )
  456.         bytcnt += au_cdDECD( linebufr[0] );
  457.       else
  458.         break;
  459.       }
  460.     }
  461.   printf( "%9ld  ", bytcnt );                   /* Print totaled byte count.*/
  462.  
  463.   if( fgets( linebufr, au_cdMAX_LINE, inF ) )   /* Try to read past "end".  */
  464.     size = ParseSize();                         /* ...and read "size" line. */
  465.  
  466.   if( size )                                    /* Print recorded size.     */
  467.     printf( "%12ld%s\n", size, (size==bytcnt)?"":" *" );
  468.   else
  469.     printf( "-Not provided-\n" );               /* No size value found.     */
  470.  
  471.   return( TRUE );
  472.   } /* Index */
  473.  
  474. void ChecksumWarn( int lineno )
  475.   /* ------------------------------------------------------------------------ **
  476.    * This function prints a checksum warning message.  Note that each file
  477.    * is allowed a maximum of MAXCS_WARN warnings.
  478.    *
  479.    *  Input:  lineno  - The line number.  This will be printed as part of the
  480.    *                    error message.
  481.    *
  482.    *  Output: None.
  483.    * ------------------------------------------------------------------------ **
  484.    */
  485.   {
  486.   badcsums++;
  487.   if( badcsums <= MAXCS_WARN )
  488.     {
  489.     fprintf( stderr, "Warning: Bad checksum at encoded line #%d.\n", lineno );
  490.     if( badcsums == MAXCS_WARN )
  491.       fprintf( stderr, "         Maximum checksum warnings reached.\n" );
  492.     }
  493.   } /* ChecksumWarn */
  494.  
  495. boolean Decode( FILE *inF )
  496.   /* ------------------------------------------------------------------------ **
  497.    * This function decodes a single encoded file, from 'begin' line to 'end'
  498.    * and (if present) 'size'.
  499.    *
  500.    *  Input:  inF - Input file handle.
  501.    *
  502.    *  Output: TRUE if decoding was successful, else FALSE.
  503.    * ------------------------------------------------------------------------ **
  504.    */
  505.   {
  506.   FILE *fOut = stdout;
  507.   char *name;
  508.   int   mode   = 0;
  509.   int   size   = 0;
  510.   int   lncnt  = 0;
  511.   int   ErrCD;
  512.   long  cnt;
  513.   long  totcnt = 0;
  514.  
  515.   badcsums = 0;                             /* Init checksum warning count.   */
  516.   name = ParseBegin( linebufr, &mode );     /* Get filename & mode from input */
  517.   if( OutFlMode )                           /* Override mode? */
  518.     mode = OutFlMode;
  519.  
  520.   if( StdOutFlg )                           /* Override filename with stdout? */
  521.     name = "Standard output";
  522.   else
  523.     {
  524.     if( OutFlName )                         /* Override filename with -n?     */
  525.       name = OutFlName;
  526.     else
  527.       name = strdup( name );
  528.     fOut = fopen( name, (AppendFlg)?"ab":"wb" );
  529.     if( !fOut )
  530.       {
  531.       fprintf( stderr, "Error: Unable to open output file: [%s].\n", name );
  532.       return( FALSE );
  533.       }
  534.     }
  535.  
  536.   if( StatusFlg )
  537.     /* Status "ON" version. */
  538.     {
  539.     for(;;)
  540.       {
  541.       if( !(fgets( linebufr, au_cdMAX_LINE, inF )) )
  542.         {                                     /* EOF before end/n = error.*/
  543.         fprintf( stderr,
  544.                  "Error: Found End-of-File before end of encoded data.\n" );
  545.         return( FALSE );
  546.         }
  547.       if( strncmp( "end\n", linebufr, 4 ) )
  548.         {
  549.         lncnt++;
  550.         (void)au_cdCleanln( linebufr, au_cdMAX_LINE, tmpbufr, au_cdMAX_LINE );
  551.         cnt = au_cdDecodeln( tmpbufr, linebufr, au_cdMAX_LINE, &ErrCD );
  552.         if( au_cdErr_Checksum == ErrCD )
  553.           ChecksumWarn( lncnt );
  554.         if( 1 != fwrite( linebufr, cnt, 1, fOut ) )
  555.           {
  556.           fprintf( stderr, "Error: Failure writing to output file.\n" );
  557.           perror( "Error" );
  558.           return( FALSE );
  559.           }
  560.         totcnt += cnt;
  561.         if( 0 == (lncnt % 10) )
  562.           fprintf( stderr, "%-30.30s: %9ld\r", name, totcnt );
  563.         }
  564.       else
  565.         break;
  566.       }
  567.     fprintf( stderr, "%-30.30s: %9ld bytes.\n", name, totcnt );
  568.     }
  569.   else
  570.     /* Status "OFF" version (identical to above, but without status output). */
  571.     {
  572.     for(;;)
  573.       {
  574.       if( !(fgets( linebufr, au_cdMAX_LINE, inF )) )
  575.         {                                     /* EOF before end/n = error.*/
  576.         fprintf( stderr,
  577.                  "Error: Found End-of-File before end of encoded data.\n" );
  578.         return( FALSE );
  579.         }
  580.       if( strncmp( "end\n", linebufr, 4 ) )
  581.         {
  582.         lncnt++;
  583.         (void)au_cdCleanln( linebufr, au_cdMAX_LINE, tmpbufr, au_cdMAX_LINE );
  584.         cnt = au_cdDecodeln( tmpbufr, linebufr, au_cdMAX_LINE, &ErrCD );
  585.         if( au_cdErr_Checksum == ErrCD )
  586.           ChecksumWarn( lncnt );
  587.         if( 1 != fwrite( linebufr, cnt, 1, fOut ) )
  588.           {
  589.           fprintf( stderr, "Error: Failure writing to output file.\n" );
  590.           perror( "system" );
  591.           return( FALSE );
  592.           }
  593.         totcnt += cnt;
  594.         }
  595.       else
  596.         break;
  597.       }
  598.     }
  599.  
  600.   if( !StdOutFlg )
  601.     {
  602.     fclose( fOut );
  603.     /* mode handling goes here. */
  604.     /* chmod( name, mode );     */
  605.     }
  606.  
  607.   if( fgets( linebufr, au_cdMAX_LINE, inF ) )   /* Try to read past "end".  */
  608.     size = ParseSize();                         /* ...and read "size" line. */
  609.  
  610.   if( size && (size != totcnt) )
  611.     {
  612.     fprintf( stderr, "Warning: Decoded size [%d] ", totcnt );
  613.     fprintf( stderr, "does not match original size [%d].\n", size );
  614.     }
  615.  
  616.   return( TRUE );
  617.   } /* Decode */
  618.  
  619. long ScanFile( FILE *inF )
  620.   /* ------------------------------------------------------------------------ **
  621.    * This function scans the input file (inF) for 'begin' lines (which
  622.    * indicate the start of a uuencoded file).  For each 'begin' line, this
  623.    * function calls either Decode() or Index() to process the encoded file.
  624.    *
  625.    *  Input:  inF - Input file handle.
  626.    *
  627.    *  Output: The number of encoded files found and processed.
  628.    * ------------------------------------------------------------------------ **
  629.    */
  630.   {
  631.   boolean tmp = TRUE;         /* Did Decode() or Index() succeed/fail?    */
  632.   long  count = 0;            /* Number of files processed.               */
  633.  
  634.   while( fgets( linebufr, au_cdMAX_LINE, inF ) )        /* While not EOF  */
  635.     {
  636.     while( 0 == strncmp( "begin", linebufr, 5 ) )
  637.       {
  638.       /* While current line is "begin". */
  639.       count++;
  640.       if( (0 != SelectOcc) && (SelectOcc != count) )
  641.         {
  642.         /* If we've selected a specific occurance, and this isn't it, then
  643.          * skip this one.
  644.          */
  645.         if( !fgets( linebufr, au_cdMAX_LINE, inF ) )
  646.           return( count );     /* return if there are no more to be done. */
  647.         }
  648.       else
  649.         {
  650.         /* These functions will each read one entire encoded block. */
  651.         if( DecodeFlg )
  652.           {
  653.           Decode( inF );
  654.           if( badcsums > 0 )
  655.             fprintf( stderr, "Warning: There were %d checksum errors.\n",
  656.                      badcsums );
  657.           }
  658.         else
  659.           {
  660.           printf( "%5d  ", count );               /* Print index number.      */
  661.           Index( inF );
  662.           }
  663.         /* Exit on error or if we've found & processed the selected occurance.*/
  664.         if( !tmp || (SelectOcc == count) )
  665.           return( count );
  666.         } /* else */
  667.       } /* while */
  668.     } /* while */
  669.  
  670.   return( count );
  671.   } /* ScanFile */
  672.  
  673. int main( int argc, char *argv[] )
  674.   /* ------------------------------------------------------------------------ **
  675.    * Mainline.
  676.    * ------------------------------------------------------------------------ **
  677.    */
  678.   {
  679.   FILE *inF = stdin;
  680.   long  count;
  681.   long  t   = time( NULL );
  682.  
  683.   if( !ParseCmdLine( argc, argv ) )         /* Parse the command line. */
  684.     return( 0 );                            /* If parse failed, exit.  */
  685.  
  686.   if( (OutFlName) && (0 == SelectOcc) )     /* Default action.  If the user   */
  687.     SelectOcc = 1;                          /* specified an output file name, */
  688.                                             /* but didn't specify a specific  */
  689.                                             /* file occurance, we assume file */
  690.                                             /* occurance 1. */
  691.  
  692.   if( SrcFlName )                           /* Open user-specified source file*/
  693.     {
  694.     inF = fopen( SrcFlName, "rb" );
  695.     if( !(inF) )
  696.       {
  697.       fprintf( stderr, "Error: Unable to open file \"%s\".\n", SrcFlName );
  698.       return( 0 );
  699.       }
  700.     }
  701.  
  702.   if( !(DecodeFlg) )  /* Display index header. */
  703.     {
  704.     printf( "\nSource File: %s\n", (SrcFlName)?(SrcFlName):("Standard Input") );
  705.     printf( "Index  Name                                 Mode" );
  706.     printf( "    Bytes    Reported Bytes\n" );
  707.     printf( "-----  -----------------------------------  ----" );
  708.     printf( "  ---------  --------------\n" );
  709.     }
  710.  
  711.   count = ScanFile( inF );          /* Scan the input file for encoded files. */
  712.  
  713.   if( SrcFlName )                   /* Close the input file. */
  714.     fclose( inF );
  715.  
  716.   if( StatusFlg )
  717.     {
  718.     t = time( NULL ) - t;
  719.     fprintf( stderr, "\nTotal: %d encoded files processed ", count );
  720.     switch( t )
  721.       {
  722.       case 0:  fprintf( stderr, "in less than one second.\n" );
  723.                break;
  724.       case 1:  fprintf( stderr, "in 1 second.\n" );
  725.                break;
  726.       default: fprintf( stderr, "in %ld seconds.\n", t );
  727.       }
  728.     }
  729.  
  730.   return( 0 );
  731.   } /* main */
  732.  
  733. /* ========================================================================== */
  734.